package com.xzj.spring;

import com.xzj.spring.annotation.*;
import com.xzj.spring.entity.BeanDefinition;
import com.xzj.spring.entity.NamedThreadLocal;
import com.xzj.spring.process.BeanNameAware;
import com.xzj.spring.process.BeanPostProcessor;
import com.xzj.spring.process.InitializingBean;
import com.xzj.spring.utils.AnnotationUtil;
import com.xzj.springmvc.HandlerMapping;
import com.xzj.springmvc.annotation.RequestMapping;
import com.xzj.springmvc.annotation.RequestMethod;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ProjectName: SpringDemo
 * @Package: com.xzj
 * @ClassName: AnnotationApplicationContextConfig
 * @Author: xzj
 * @Description:
 * @Date: 2022/7/8 8:51
 * @Version: 1.0
 */
@Component
public class AnnotationApplicationContextConfig {

    //标记对象状态
    private final ThreadLocal prototypesCurrentlyInCreation = new NamedThreadLocal("对象在创建中");

    private volatile Object lock = new Object();

    //bean的定义信息
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //临时保存单例beanName 创建完成后清空释放内存
    private List<String> singletonNames = new ArrayList<>(256);

    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    private ConcurrentHashMap<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(256);

    //    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap(64);
//    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap(64);
    private final Map<Class<?>, List<String>> allBeanNamesByType = new ConcurrentHashMap(64);
    private final Map<Class<?>, List<String>> singletonBeanNamesByType = new ConcurrentHashMap(64);

    private ConcurrentHashMap<String, Object> proxyObjects = new ConcurrentHashMap<>(256);

    //保存手动注册的单例bean初始化 防止因为多个名称导致多次调用增强器
    private final Map<Class<?>, Object> initSingletonBean = new ConcurrentHashMap<>(256);

    //缓存所有增强器
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    //保存被代理过的对象
    private Set<String> beanProxyNames = new HashSet<>();

    public AnnotationApplicationContextConfig() {
    }

    public AnnotationApplicationContextConfig(Class<?> clz) {
        this.register(clz);
    }


    public void register(Class<?> clz) {
        String path = clz.getAnnotation(ComponentScan.class).value();
        path = path.replace(".", "/");
        URL url = Thread.currentThread().getContextClassLoader().getResource(path);
        int i = url.getPath().indexOf("target/classes");

        File file = new File(url.getPath());
        File[] files = file.listFiles();
        try {
            //深度优先遍历所有文件
            dfs(files, i + "target/classes".length());
            //创建所有非懒加载bean和被@Bean创建的bean
            createNonLazyBean(clz);
        } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param scope
     * @param clz
     * @param beanName
     * @return
     * @Author xzj
     * @Description 创建 @Bean 注解注释的bean对象
     * @Date 15:57 2022/7/13
     **/
    private void initBean(Scope scope, Class<?> clz, String beanName) {
        BeanDefinition beanDefinition = new BeanDefinition();
        if (scope != null) {
            beanDefinition.setScope(scope.value());
        } else {
            beanDefinition.setScope("singleton");
        }
        beanDefinition.setBeanClass(clz);
        beanDefinitionMap.put(beanName, beanDefinition);
        if (scope == null || "".equals(scope.value().trim()) || "singleton".equals(scope.value())) {
            if (initSingletonBean.containsKey(clz)) {
                singletonObjects.put(beanName, initSingletonBean.get(clz));
            } else {
                Object bean = createBean(beanName, beanDefinition);
                initSingletonBean.put(clz, bean);
            }
        }
    }

    /**
     * @param files
     * @param parentIndex
     * @return
     * @Author xzj
     * @Description 递归扫描包下所有文件 并对class文件 加载字节码对象 并判断是否需要容器管理
     * @Date 12:52 2022/7/8
     **/
    private void dfs(File[] files, int parentIndex) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                dfs(file.listFiles(), parentIndex);
            }
            if (file.isFile()) {
                String filePath = file.toString().substring(parentIndex);
                if (filePath.endsWith(".class")) {
                    String className = filePath.replaceAll("\\\\|/", ".");
                    className = className.replace(".class", "");
                    //获取class对象
                    Class<?> clz = Class.forName(className);
                    //是注解直接跳过
                    if (clz.isAnnotation()) {
                        continue;
                    }
                    Annotation[] annotations = clz.getAnnotations();
                    //判断有没有被标记@Component
                    if (AnnotationUtil.findAnnotation(Component.class, annotations)) {
                        //spring 核心流程 注册bean对象 并属性注入
                        springCore(clz);
                        //Spring mvc controller 流程
                        springMvc(clz, annotations);
                    }
                }
            }
        }
    }

    /**
     * @param clz
     * @param annotations
     * @return
     * @Author xzj
     * @Description springmvc 初始化流程 把所有映射保存进HandlerMapping
     * @Date 15:52 2022/7/15
     **/
    private void springMvc(Class<?> clz, Annotation[] annotations) {
        if (AnnotationUtil.findAnnotation(RequestMapping.class, annotations)) {
            RequestMapping controller = AnnotationUtil.getAnnotation(clz, RequestMapping.class);
            String[] paths = controller.value();
            //获取所有路径方法
            Method[] methods = clz.getDeclaredMethods();
            for (Method method : methods) {
                Annotation[] methodAnnotations = method.getAnnotations();
                if (AnnotationUtil.findAnnotation(RequestMapping.class, methodAnnotations)) {
                    RequestMapping requestMapping = AnnotationUtil.getAnnotation(method, RequestMapping.class);
                    //获取接收哪些请求
                    RequestMethod[] requestMethods = requestMapping.method();
                    if (requestMethods.length == 0) {
                        requestMethods = new RequestMethod[]{RequestMethod.GET, RequestMethod.POST,
                                RequestMethod.PUT, RequestMethod.HEAD, RequestMethod.PATCH,
                                RequestMethod.DELETE, RequestMethod.OPTIONS, RequestMethod.TRACE,
                        };
                    }
                    String[] pathMethods = requestMapping.value();
                    //如果controller上的路径不为空
                    if (paths != null && paths.length > 0) {
                        for (String path : paths) {
                            //判断方法上的路径不为空
                            if (pathMethods != null && pathMethods.length > 0) {
                                //controller上的路径不为空，方法上的不为空
                                for (String pathMethod : pathMethods) {
                                    HandlerMapping.setServerBean(path + pathMethod, clz);
                                    for (RequestMethod requestMethod : requestMethods) {
                                        //存入key：POST/hello/sayHello
                                        HandlerMapping.setMethod(requestMethod + path + pathMethod, method);
                                    }
                                }
                            } else {
                                //controller上的路径不为空，方法上的为空，直接添加
                                HandlerMapping.setServerBean(path, clz);
                                for (RequestMethod requestMethod : requestMethods) {
                                    HandlerMapping.setMethod(requestMethod + path, method);
                                }
                            }
                        }
                    } else {
                        //如果controller上值为空 方法上不为空
                        if (pathMethods != null && pathMethods.length > 0) {
                            for (String pathMethod : pathMethods) {
                                HandlerMapping.setServerBean(pathMethod, clz);
                                for (RequestMethod requestMethod : requestMethods) {
                                    HandlerMapping.setMethod(requestMethod + pathMethod, method);
                                }
                            }
                            //路径为“”
                        } else {
                            HandlerMapping.setServerBean("", clz);
                            for (RequestMethod requestMethod : requestMethods) {
                                HandlerMapping.setMethod(requestMethod + "", method);
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     * @param clz
     * @return
     * @Author xzj
     * @Description spring 核心流程    注册bean对象  并初始化属性值
     * @Date 11:22 2022/7/14
     **/
    private void springCore(Class<?> clz) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        //保存AOP对象
        if (BeanPostProcessor.class.isAssignableFrom(clz)) {
            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clz.getDeclaredConstructor().newInstance();
            beanPostProcessorList.add(beanPostProcessor);
            return;
        }
        String beanName = clz.getSimpleName();
        Component component = AnnotationUtil.getAnnotation(clz, Component.class);
        //获取自定义bean名称
        String value = component.value();
        if (!"".equals(value.trim())) {
            beanName = value;
        }
        //创建BeanDefinition对象 并添加进map
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClass(clz);
        //获取作用域注解
        Scope annotation = clz.getAnnotation(Scope.class);
        if (annotation == null || "".equals(annotation.value().trim())) {
            //默认单例
            beanDefinition.setScope("singleton");
            List<String> list = new ArrayList<>();
            list.add(beanName);
            singletonBeanNamesByType.put(clz, list);
        } else {
            beanDefinition.setScope(annotation.value());
        }
        //获取懒加载注解
        Lazy lazy = clz.getAnnotation(Lazy.class);
        if (lazy == null) {
            if ("singleton".equals(beanDefinition.getScope())) {
                singletonNames.add(beanName);
            }
        } else {
            beanDefinition.setLazyInit(true);
        }
        beanDefinitionMap.put(beanName, beanDefinition);
        List<String> list = new ArrayList<>();
        list.add(beanName);
        allBeanNamesByType.put(clz, list);
    }

    /**
     * @param
     * @return
     * @Author xzj
     * @Description 创建所有非懒加载的bean
     * @Date 9:17 2022/7/11
     **/
    private void createNonLazyBean(Class<?> clz) {
        for (String singletonName : singletonNames) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(singletonName);
            Object bean = createBean(singletonName, beanDefinition);
            //如果没有被依赖提前将代理对象放入
            if (!singletonObjects.containsKey(singletonName)) {
                singletonObjects.put(singletonName, bean);
            }
        }
        //释放空间
        singletonNames.clear();
        //创建被@Bean创建的bean
        Method[] methods = clz.getMethods();
        for (Method method : methods) {
            Bean beanAnnotation = method.getAnnotation(Bean.class);
            if (beanAnnotation != null) {
                //获取注解判断是单例还是原型
                Scope scope = method.getAnnotation(Scope.class);
                //调用方法并保存进beanDefinitionMap
                Class<?> returnType = method.getReturnType();
                //获取方法名称
                String[] value = beanAnnotation.value();
                if (value == null || value.length == 0) {
                    String beanName = method.getName();
                    initBean(scope, returnType, beanName);
                } else {
                    for (String beanName : value) {
                        initBean(scope, returnType, beanName);
                    }
                }
            }
        }
    }

    /**
     * @param beanDefinition
     * @return
     * @Author xzj
     * @Description 创建bean对象
     * @Date 15:02 2022/7/8
     **/
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Object beanClass = beanDefinition.getBeanClass();
        String scope = beanDefinition.getScope();
        Class clz = null;
        try {
            if (beanClass instanceof Class) {
                clz = (Class) beanClass;
            } else if (beanClass instanceof String) {
                clz = Class.forName((String) beanClass);
            }
            Object bean;
            if (!"singleton".equals(scope)) {
                //实例化对象
                Constructor<?> constructor = clz.getDeclaredConstructor(new Class[]{});
                constructor.setAccessible(true);
                bean = constructor.newInstance(new Object[]{});
                propertyFill(bean, clz.getDeclaredFields());
            } else {
                //标记对象在创建中
                beforePrototypeCreation(clz.getName());
                //创建对象并返回
                bean = doCreateBean(beanName, clz);
            }
            //创建bean之后 判断bean对象是否实现了 BeanNameAware 等接口
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (!"singleton".equals(scope) || !beanProxyNames.contains(beanName)) {
                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                    beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
                }
            }
            //创建bean之后 判断bean对象是否实现了 InitializerAware 等接口
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            if (!"singleton".equals(scope) || !beanProxyNames.contains(beanName)) {
                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                    bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
                }
                beanProxyNames.add(beanName);
                //把代理对象放入缓存池
                if ("singleton".equals(scope)) {
                    singletonObjects.put(beanName, bean);
                }
            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //删除对象在创建中标记
            afterPrototypeCreation(clz.getName());
        }
    }

    /**
     * @param clz
     * @return
     * @Author xzj
     * @Description 根据class创建对象
     * @Date 12:50 2022/7/8
     **/
    private Object doCreateBean(String beanName, Class<?> clz) {
        try {
            //从一级缓存中拿
            Object bean = singletonObjects.get(clz.getName());
            if (bean != null) {
                return bean;
            }
            //从二级缓存中拿，且判断对象是否在实例化过程中
            bean = earlySingletonObjects.get(clz.getName());
            if (isSingletonCurrentlyInCreation(clz.getName()) && bean != null) {
                return bean;
            }
            synchronized (lock) {
                bean = singletonObjects.get(clz.getName());
                if (bean != null) {
                    return bean;
                }
                bean = earlySingletonObjects.get(clz.getName());
                if (isSingletonCurrentlyInCreation(clz.getName()) && bean != null) {
                    return bean;
                }
                //实例化对象
                Constructor<?> constructor = clz.getDeclaredConstructor(new Class[]{});
                constructor.setAccessible(true);
                bean = constructor.newInstance(new Object[]{});
                earlySingletonObjects.put(clz.getName(), bean);
                propertyFill(bean, clz.getDeclaredFields());
                //从二级缓存中移除
                earlySingletonObjects.remove(clz.getName());
                //如果这个时候发现自己被代理了 删除对象，获取代理对象 并对代理对象属性填充
                if (singletonObjects.containsKey(beanName)) {
                    bean = singletonObjects.get(beanName);
                    propertyFill(bean, clz.getDeclaredFields());
                } else {
                    singletonObjects.put(clz.getName(), bean);
                }

            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param className
     * @return
     * @Author xzj
     * @Description 对象是否在实例化过程中
     * @Date 16:23 2022/7/11
     **/
    private boolean isSingletonCurrentlyInCreation(String className) {
        Object prototypeNames = prototypesCurrentlyInCreation.get();
        if (prototypeNames == null) {
            return false;
        } else if (prototypeNames instanceof String) {
            return className.equals(prototypeNames);
        } else if (prototypeNames instanceof Set) {
            return ((Set<String>) prototypeNames).contains(className);
        }
        return false;
    }

    /**
     * @param className
     * @param clz
     * @return
     * @Author xzj
     * @Description 获得bean
     * @Date 12:51 2022/7/8
     **/
    public <T> T getBean(String className, Class<T> clz) {
        return (T) getBean(className);
    }

    /**
     * @param clz
     * @return
     * @Author xzj
     * @Description
     * @Date 20:33 2022/7/11
     **/
    public <T> T getBean(Class<T> clz) {
        List<String> list = allBeanNamesByType.get(clz);
        if (list != null && list.size() > 0) {
            for (String s : list) {
                return (T) getBean(s);
            }
        }
        return (T) getBean(clz.getName());
    }

    /**
     * @param className
     * @return
     * @Author xzj
     * @Description 获得bean
     * @Date 12:52 2022/7/8
     **/
    public Object getBean(String className) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(className);
        if (beanDefinition == null) {
            return null;
        }
        Object bean = singletonObjects.get(className);
        if (bean != null) {
            return bean;
        }
        return createBean(className, beanDefinition);
    }

    /**
     * @param className
     * @return
     * @Author xzj
     * @Description 删除标记
     * @Date 17:16 2022/7/8
     **/
    private void afterPrototypeCreation(String className) {
        Object curVal = prototypesCurrentlyInCreation.get();
        if (curVal instanceof String) {
            prototypesCurrentlyInCreation.remove();
        } else if (curVal instanceof Set) {
            Set<String> beanNameSet = (Set<String>) curVal;
            beanNameSet.remove(className);
            if (beanNameSet.isEmpty()) {
                prototypesCurrentlyInCreation.remove();
            }
        }
    }

    /**
     * @param className
     * @return
     * @Author xzj
     * @Description 标记对象正在创建中
     * @Date 17:16 2022/7/8
     **/
    private void beforePrototypeCreation(String className) {
        //设置对象在创建中
        Object o = prototypesCurrentlyInCreation.get();
        if (o == null) {
            //如果没有对象在创建直接存
            prototypesCurrentlyInCreation.set(className);
        } else if (o instanceof String) {
            //如果有一个在创建中的对象 新建一个set集合存入
            String o1 = (String) prototypesCurrentlyInCreation.get();
            Set<String> set = new HashSet<>();
            set.add(o1);
            set.add(className);
            prototypesCurrentlyInCreation.set(set);
        } else if (o instanceof Set) {
            Set<String> set = (Set<String>) prototypesCurrentlyInCreation.get();
            set.add(className);
        }
    }

    /**
     * @param obj
     * @param declaredFields
     * @return
     * @Author xzj
     * @Description 属性填充
     * @Date 14:28 2022/7/8
     **/
    private void propertyFill(Object obj, Field[] declaredFields) {
        try {
            Field[] fields = declaredFields;
            for (Field field : fields) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (annotation != null) {
                    Class<?> fieldType = field.getType();
                    Object bean = getBean(fieldType);
                    field.setAccessible(true);
                    field.set(obj, bean);
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e.getCause());
        }
    }
}
